package org.nobject.common.code.describer;

import static org.nobject.common.lang.StringUtils.isEmpty;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.nobject.common.exception.ParamException;
import org.nobject.common.exception.UnSupportException;
import org.nobject.common.lang.ArrayUtils;
import org.nobject.common.lang.ClassUtils;
import org.nobject.common.lang.StringUtils;
import org.nobject.common.regex.ValidatorUtils;

/**
 * RPC参数校验器
 *
 * @author bianrongjun
 * @version 1.0
 */
public class ParamDescriber {
	/** 校验对应的参数索引 */
	public int idx=-1;
	/** 名称 */
	public String name;
	/** 参数名称或参数键值对应的名称 */
	public String comment;
	/** 是否能为空 */
	public boolean unnull=false;
	/** 校验 */
	public String validate;
	/** 长度 */
	public int length;
	/** 校验提示 */
	public String message;
	/** 键值 */
	public String key;
	/** 键值来源 */
	public Class keyFrom;
	/** 子键来源 */
	public Class subKeyFrom;
	/** 类型 */
	public Class type;
	/** 使用Field校验 */
	public boolean vfield;
	/** 过滤 */
	public boolean filter;
	/** 子描述s */
	public Object subDescs;
	
	/**
	 * 构造函数 
	 */
	public ParamDescriber(){
		
	}
	
	/**
	 * 构造函数 
	 * 第一层参数不会是键值
	 */
	public ParamDescriber(Object desc) {
		this(
			DescriberUtils.getName		(desc), 
			DescriberUtils.getIdx		(desc), 
			DescriberUtils.getComment	(desc), 
			DescriberUtils.getUnnull	(desc), 
			DescriberUtils.getValidate	(desc), 
			DescriberUtils.getLength	(desc), 
			DescriberUtils.getMessage	(desc), 
			DescriberUtils.getKey		(desc),
			DescriberUtils.getKeyFrom	(desc),
			DescriberUtils.getSubKeyFrom(desc),
			DescriberUtils.getType		(desc),
			DescriberUtils.getVfield	(desc),
			DescriberUtils.getFilter	(desc),
			DescriberUtils.getSubs		(desc)
		);
	}
	
	/**
	 * 构造函数 
	 * @param name
	 * @param idx
	 * @param comment
	 * @param unnull
	 * @param validate
	 * @param length
	 * @param message
	 * @param key
	 * @param keyFrom
	 * @param subKeyFrom
	 * @param vfield
	 * @param filter
	 * @param subDescs
	 */
	public ParamDescriber (
			String name,
			int idx, 
			String comment, 
			boolean unnull, 
			String validate, 
			int length, 
			String message, 
			String key,
			Class keyFrom,
			Class subKeyFrom,
			Class type,
			boolean vfield,
			boolean filter,
			Object subDescs) {
		this.name		=name;
		this.idx 		=idx;
		this.comment 	=comment;
		this.unnull 	=unnull;
		this.validate 	=validate;
		this.length 	=length;
		this.message 	=message;
		this.key 		=key;
		this.keyFrom	=keyFrom;
		this.subKeyFrom	=subKeyFrom;
		this.type		=type;
		this.vfield		=vfield;
		this.filter		=filter;
		this.subDescs	=subDescs;
	}
	
	/**
	 * 构造函数 
	 * @param idx
	 * @param comment
	 * @param unnull
	 * @param validate
	 * @param length
	 * @param message
	 * @param key
	 * @param vfield
	 * @param desc
	 * @param paramType
	 */
	public ParamDescriber readKeyFrom() {
		
		//STN:fieldDesc
		if(this.keyFrom!=null&&this.keyFrom!=CNull.class&&!isEmpty(this.key)&&this.vfield){
			Field f=ClassUtils.getField(keyFrom, key);
			if(f==null) throw new RuntimeException("未在"+this.keyFrom.getName()+"找到"+key);
			FieldDesc fieldDesc=(FieldDesc)f.getAnnotation(FieldDesc.class);
			if(fieldDesc==null) throw new RuntimeException(this.keyFrom.getName()+"#"+key+"不含FieldDesc");
			
			if(StringUtils.isEmpty(this.comment)) 		this.comment		=fieldDesc.comment();
			if(StringUtils.isEmpty(this.validate)) 		this.validate		=fieldDesc.validate();
			if(this.length<0) 							this.length			=fieldDesc.length();
			if(this.type==null||this.type.equals(CNull.class))	this.type	=f.getType();
		}
		
		return this;
	}
	
	
	
	/** 
	 * 校验
	 * @param o
	 * @throws ParamException
	 * @throws UnSupportException
	 */
	public void valide(Object o) throws ParamException{
		//是否为空
		if(this.unnull&&o==null) throw new ParamException(this.comment+"不能为空");
		if(o==null) return;
		
		boolean isMap	=o instanceof Map;
		boolean isList	=o instanceof List;
		
		Class c=o.getClass();
		
		//类型校验
		if(this.type!=null&&this.type.equals(CNull.class)){
			boolean typeError=false;
			if(ClassUtils.isNumber(this.type)){
				if(!ClassUtils.isNumber(c)) typeError=true;
			}else{
				if(!ClassUtils.isEqualImpl(this.type, c)) typeError=true;
			}
			if(typeError) throw new ParamException(StringUtils.notEmpty(this.comment,this.name,this.key,"参数")+"不能为空");
		}
		
		
		//格式校验
		//字符串
		try {
			if(ClassUtils.isString(c)){
				String sO=(String)o;
				//非空判断
				if(isEmpty(sO)){
					if(this.unnull) throw new ParamException(this.comment+"不能为空");
				}else{
					//值不为空时进行格式校验
					if(!isEmpty(this.validate)&&!ValidatorUtils.isMatch(this.validate, sO)){
						throw new ParamException(this.comment+"格式不正确");
					}
					
					//自动截长度
					if(this.length>=0) o=StringUtils.substr(sO, this.length);
				}
			}else if(ClassUtils.isNumber(c)){
				//值不为空时进行格式校验
				if(!isEmpty(this.validate)&&!ValidatorUtils.isMatch(this.validate, o)){
					throw new ParamException(this.comment+"格式不正确");
				}
			}else if(ClassUtils.isBoolean(c)){
//				if(!isEmpty(this.validate)&&!ValidatorUtils.isMatch(this.validate, o)){
//					throw new ParamException(this.comment+"格式不正确");
//				}
//				throw new ParamException("暂不支持布尔校验");
			}else if(ClassUtils.isCollection(c)){
				//非空判断
				if(this.unnull&&((Collection)o).size()==0) throw new ParamException(this.comment+"不能为空");
			}else if(ClassUtils.isMap(c)){
				//非空判断
				if(this.unnull&&((Map)o).keySet().size()==0) throw new ParamException(this.comment+"不能为空");
			}else if(c.isArray()){
				Object[] oo=(Object[])o;
				boolean isE=ArrayUtils.hasEmpty(oo);
				//非空判断
				if(this.unnull&&isE) throw new ParamException(this.comment+"不能为空");
				//TODO 待支持数组字符串的格式校验
			}else{
				throw new ParamException("暂不支持其它格式校验");
			}
		} catch (UnSupportException e) {
			throw new ParamException(e);
		}
		
		
		//2.子递归校验
		int l=this.subDescs!=null?Array.getLength(this.subDescs):0;
		if(l>0&&(!isMap&&!isList)){
			throw new ParamException("目标数据非Map、List类型的数据");
		}
		Set subKeys=new HashSet();
		for (int i = 0; i < l; i++) {
			Object subDesc=Array.get(subDescs, i);
			ParamDescriber pv=new ParamDescriber(subDesc);
			if(pv.keyFrom==null||pv.keyFrom.equals(CNull.class)){
				pv.keyFrom=subKeyFrom;
			}
			pv.readKeyFrom();
			
			Object subO=null;
			if(isMap){
				subO=((Map)o).get(pv.key);
				subKeys.add(pv.key);
			}else{
				if(pv.idx<0){
					pv.idx=i;
				}
				subO=((List)o).get(pv.idx);
			}
			
			//TODO 缓存Validator
			pv.valide(subO);
		}
		
		//3.多余参数过滤
		if(isMap&&l>0&&this.filter){
			String[] ks=(String[])ArrayUtils.toArray(((Map<String,Object>)o).keySet(), String.class);
			for(String k :ks){
				if(!subKeys.contains(k)){
					((Map)o).remove(k);
				}
			}
		}
		
	}
	
	/** 
	 * toMap
	 * @return
	 */
	public Map toMap(){
		
		Map m=new LinkedHashMap();
		String [] ks={
			"idx"		,
			"name"		,
			"key"		,
	        "keyFrom"	,
	        "type"		,
			"comment"	,
			"unnull"	,
	        "validate"	,
	        "length" 	,
	        "message"	,
	        "subKeyFrom",	
	        "vfield"	,
	        "filter"	,
	        "subDescs"
		};
		
		Class c=ParamDescriber.class;
		
		for(String k:ks){
			
			Object o;
			try {
				o = c.getField(k).get(this);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			
			//转换Desc类型
			if(o instanceof Class){
				o=StringUtils.toString0(o);
			}
			
			if(o==null) continue;
			Class oc=o.getClass();
			if(oc.isArray()&&oc.getComponentType().getSimpleName().endsWith("Desc")){
				int l=Array.getLength(o);
				List subs=new LinkedList();
				for (int i = 0; i < l; i++) {
					Object subDesc=Array.get(o,i);
					
					ParamDescriber pd=new ParamDescriber(subDesc);
					if(pd.keyFrom==null||pd.keyFrom.equals(CNull.class)){
						pd.keyFrom=this.subKeyFrom;
					}
					pd.readKeyFrom();
					
					subs.add(pd.toMap());
				}
				o=subs;
			}
			m.put(k, o);
		}
		
		return m;
	}
}
